import fs from 'fs'
import path from 'path'

import { beforeAll, afterAll, test, expect } from 'vitest'

import { ensurePosixPath } from '@redwoodjs/project-config'

import { findCells, findDirectoryNamedModules } from '../files'
import {
  generateMirrorCells,
  generateMirrorDirectoryNamedModules,
  generateTypeDefRouterPages,
  generateTypeDefCurrentUser,
  generateTypeDefRouterRoutes,
  generateTypeDefGlobImports,
  generateTypeDefGlobalContext,
  mirrorPathForDirectoryNamedModules,
  mirrorPathForCell,
  generateTypeDefScenarios,
} from '../generate/typeDefinitions'

const FIXTURE_PATH = path.resolve(
  __dirname,
  '../../../../__fixtures__/example-todo-main',
)

beforeAll(() => {
  process.env.RWJS_CWD = FIXTURE_PATH
})
afterAll(() => {
  delete process.env.RWJS_CWD
})

const cleanPaths = (p) => {
  return ensurePosixPath(path.relative(FIXTURE_PATH, p))
}

test('generate the correct mirror types for cells', () => {
  const paths = generateMirrorCells()
  const p = paths.map(cleanPaths)

  expect(p).toMatchInlineSnapshot(`
    [
      ".redwood/types/mirror/web/src/components/NumTodosCell/index.d.ts",
      ".redwood/types/mirror/web/src/components/NumTodosTwoCell/index.d.ts",
      ".redwood/types/mirror/web/src/components/TodoListCell/index.d.ts",
    ]
  `)

  expect(fs.readFileSync(paths[0], 'utf-8')).toMatchInlineSnapshot(`
    "// This file was generated by RedwoodJS
    import * as Cell from './NumTodosCell'
    import type { CellProps } from '@redwoodjs/web'
    import type { NumTodosCell_GetCount, NumTodosCell_GetCountVariables } from 'types/graphql'

    type SuccessType = typeof Cell.Success

    export * from './NumTodosCell'

    type CellInputs = CellProps<SuccessType, NumTodosCell_GetCount, typeof Cell, NumTodosCell_GetCountVariables>

    export default function (props: CellInputs): ReturnType<SuccessType>

    //# sourceMappingURL=index.d.ts.map
    "
  `)
})

test('generate the correct mirror types for directory named modules', () => {
  const paths = generateMirrorDirectoryNamedModules()
  const p = paths.map(cleanPaths)

  expect(p).toMatchInlineSnapshot(`
    [
      ".redwood/types/mirror/web/src/graphql/index.d.ts",
      ".redwood/types/mirror/api/src/directives/requireAuth/index.d.ts",
      ".redwood/types/mirror/api/src/directives/skipAuth/index.d.ts",
      ".redwood/types/mirror/api/src/functions/healthz/index.d.ts",
      ".redwood/types/mirror/api/src/functions/nested/index.d.ts",
      ".redwood/types/mirror/api/src/services/todos/index.d.ts",
      ".redwood/types/mirror/web/src/components/AddTodo/index.d.ts",
      ".redwood/types/mirror/web/src/components/AddTodoControl/index.d.ts",
      ".redwood/types/mirror/web/src/components/Check/index.d.ts",
      ".redwood/types/mirror/web/src/components/TableCell/index.d.ts",
      ".redwood/types/mirror/web/src/components/TodoItem/index.d.ts",
      ".redwood/types/mirror/web/src/layouts/SetLayout/index.d.ts",
      ".redwood/types/mirror/web/src/pages/BarPage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/FatalErrorPage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/FooPage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/HomePage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/NotFoundPage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/PrivatePage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/TypeScriptPage/index.d.ts",
      ".redwood/types/mirror/web/src/pages/admin/EditUserPage/index.d.ts",
    ]
  `)

  expect(fs.readFileSync(paths[0], 'utf-8')).toMatchInlineSnapshot(`
      "// This file was generated by RedwoodJS
      import { default as DEFAULT } from './graphql'
      export default DEFAULT
      export * from './graphql'

      //# sourceMappingURL=index.d.ts.map
      "
  `)

  expect(fs.readFileSync(paths[0], 'utf-8')).toMatchInlineSnapshot(`
    "// This file was generated by RedwoodJS
    import { default as DEFAULT } from './graphql'
    export default DEFAULT
    export * from './graphql'

    //# sourceMappingURL=index.d.ts.map
    "
  `)
})

test('generates global page imports', () => {
  const paths = generateTypeDefRouterPages()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/web-routesPages.d.ts')

  const c = fs.readFileSync(paths[0], 'utf-8')

  expect(c).toContain(`
declare global {
  const BarPage: typeof BarPageType
  const FatalErrorPage: typeof FatalErrorPageType
  const FooPage: typeof FooPageType
  const HomePage: typeof HomePageType
  const NotFoundPage: typeof NotFoundPageType
  const PrivatePage: typeof PrivatePageType
  const TypeScriptPage: typeof TypeScriptPageType
  const adminEditUserPage: typeof adminEditUserPageType
}`)
})

test('generates global page imports source maps', () => {
  const paths = generateTypeDefRouterPages()
  const sourceMap = JSON.parse(fs.readFileSync(paths[0] + '.map', 'utf-8'))
  sourceMap.sources = sourceMap.sources.map((source) => ensurePosixPath(source))
  expect(sourceMap).toMatchSnapshot()
})

test('generate current user ', () => {
  const paths = generateTypeDefCurrentUser()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/all-currentUser.d.ts')
  // The type definition output is static, so there's nothing to test.
})

test('generates the router routes', () => {
  const paths = generateTypeDefRouterRoutes()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/web-routerRoutes.d.ts')

  const c = fs.readFileSync(paths[0], 'utf-8')
  expect(c).toContain(`
    home: (params?: RouteParams<"/"> & QueryParams) => "/"
    typescriptPage: (params?: RouteParams<"/typescript"> & QueryParams) => "/typescript"
    someOtherPage: (params?: RouteParams<"/somewhereElse"> & QueryParams) => "/somewhereElse"
    fooPage: (params?: RouteParams<"/foo"> & QueryParams) => "/foo"
    barPage: (params?: RouteParams<"/bar"> & QueryParams) => "/bar"
    privatePage: (params?: RouteParams<"/private-page"> & QueryParams) => "/private-page"
`)
})

test('generates source maps for the router routes', () => {
  const paths = generateTypeDefRouterRoutes()
  const sourceMap = JSON.parse(fs.readFileSync(paths[0] + '.map', 'utf-8'))
  sourceMap.sources = sourceMap.sources.map((source: string) =>
    ensurePosixPath(source),
  )
  expect(sourceMap).toMatchSnapshot()
})

test('generate glob imports', () => {
  const paths = generateTypeDefGlobImports()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/api-globImports.d.ts')
})

test('generate api global context', () => {
  const paths = generateTypeDefGlobalContext()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/api-globalContext.d.ts')
})

test('generate scenario type defs', () => {
  const paths = generateTypeDefScenarios()
  const p = paths.map(cleanPaths)
  expect(p[0]).toEqual('.redwood/types/includes/api-scenarios.d.ts')
})

test('mirror path for directory named modules', () => {
  const d = findDirectoryNamedModules()
  const p = mirrorPathForDirectoryNamedModules(d[0])

  expect(cleanPaths(p[0])).toMatchSnapshot()
  expect(cleanPaths(p[1])).toMatchSnapshot()
})

test('mirror path for dir cells', () => {
  const c = findCells()
  const p = mirrorPathForCell(c[0])

  expect(cleanPaths(p[0])).toMatchInlineSnapshot(
    `".redwood/types/mirror/web/src/components/NumTodosCell"`,
  )
})
